package com.letoken.platform.admin.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.letoken.platform.admin.async.AdminOperationLogAsync;
import com.letoken.platform.admin.service.AdminUserService;
import com.letoken.platform.pub.enums.AdminOperationLogTypeEnum;
import com.letoken.platform.repo.admin.po.AdminOperationLog;
import com.letoken.platform.repo.utlis.WebUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * WebLogsAspect
 *
 * @Author: letokenlabs
 * @Date: 2021/2/18 10:23
 */
@Slf4j
@Aspect
@Component
public class WebLogsAspect {

    @Autowired
    private AdminOperationLogAsync adminOperationLogAsync;

    @Autowired
    private AdminUserService adminUserService;


    private long startTime = 0L;

    /**
     * 以 controller 包下定义的所有请求为切入点
     */
    @Pointcut("execution(public * com.letoken.platform.admin.controller.*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore() {
        startTime = System.currentTimeMillis();
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String logUrl = request.getRequestURI();
        String classPath = pjp.getSignature().getDeclaringTypeName();
        String executeMethod = pjp.getSignature().getName();
        String logCoreParams = "";
        if (pjp.getArgs() != null){
            if (pjp.getArgs().length == 1){
                if (pjp.getArgs()[0] instanceof MultipartFile){
                    logCoreParams = "file";
                }else {
                    logCoreParams = JSON.toJSONString(pjp.getArgs()[0]);
                }
            }else {
                for (int i = 0 ; i < pjp.getArgs().length ; i++){
                    if (pjp.getArgs()[i] instanceof MultipartFile){
                        logCoreParams = "file";
                        break;
                    }
                }
                if (!StringUtils.hasLength(logCoreParams)){
                    logCoreParams = JSONArray.toJSONString(pjp.getArgs());
                }

            }
        }
        Object result = pjp.proceed();
        String logResult = JSON.toJSONString(result);
        Integer operationUserId;
        //根据请求头token获取当前用户信息
        operationUserId = WebUtils.getAdminUserId();
        if ("/user/login".equals(logUrl)){
            JSONObject jsonObject = JSON.parseObject(logResult);
            JSONObject data = jsonObject.getJSONObject("data");
            Integer userId = data.getInteger("userId");
            if (operationUserId == null){
                operationUserId = userId;
            }
        }

        long costTime = System.currentTimeMillis() - startTime;
        String[] pathName = logUrl.split("/");
        String logDescription = null;

//        // 打印请求相关参数
//        log.info("========================================== Start ==========================================");
//        // 打印请求 url
//        log.info("URL            : {}", logUrl);
//        // 打印 Http method
//        log.info("HTTP Method    : {}", request.getMethod());
//        // 打印调用 controller 的全路径以及执行方法
//        log.info("Class Method   : {}.{}", classPath, executeMethod);
//        // 打印请求的 IP
//        log.info("IP             : {}", request.getRemoteAddr());
//        // 打印请求入参
//        log.info("Request Args   : {}", logCoreParams);
//        // 打印出参
//        log.info("Response Args  : {}", logResult);
        // 执行耗时
        log.info("Time-Consuming : {} ms", costTime);
//        log.info("=========================================== End ===========================================");
//        log.info("");

        //设置描述，获取请求路径方法上的，通过反射获取到目标类上的swagger注解
        Class<?> targetClass = Class.forName(classPath);
        Method[] methods = targetClass.getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().contains(executeMethod)) {
                boolean annotationPresent = method.isAnnotationPresent(ApiOperation.class);
                if (annotationPresent) {
                    // 获取自定义注解对象
                    ApiOperation apiAno = method.getAnnotation(ApiOperation.class);
                    logDescription = apiAno.value();
                }
            }
        }

        AdminOperationLog adminOperationLog = new AdminOperationLog();
        adminOperationLog.setLogCoreParams(logCoreParams);
        adminOperationLog.setLogParams(logCoreParams);
        adminOperationLog.setLogUrl(logUrl);
        adminOperationLog.setLogModuleName(pathName[2]);  //设置操作模块名称
        adminOperationLog.setLogType(AdminOperationLogTypeEnum.getLogTypeCode(executeMethod)); //设置操作类型
        adminOperationLog.setLogModule(1);      //操作模块
        adminOperationLog.setLogPath(classPath + "#" + executeMethod); //代码路径
        adminOperationLog.setLogResult(logResult); //操作结果(出参)
        adminOperationLog.setOperationUserId(operationUserId); //操作人
        adminOperationLog.setLogDescription(logDescription); //描述
        adminOperationLog.setCreateTime(LocalDateTime.now());
        adminOperationLog.setCostTime(String.valueOf(costTime));
        adminOperationLog.setLogLevel(0);
        adminOperationLog.setIsDelete(0);
        adminOperationLogAsync.insertAdminOperationLog(adminOperationLog);
        return result;
    }


}
